/*
 * Copyright (C) 2016 Allwinner.
 * wangwei <wangwei@allwinnertech.com>
 *
 * SUNXI AXP1506  Driver
 *
 * SPDX-License-Identifier: GPL-2.0+
 */


#include "asm/arch/rsb.h"

#undef rsb_printk
#define rsb_printk(format, arg...)

static struct rsb_info rsbc = {1, 1, 1};

#if (defined FPGA_PLATFORM)
static void rsb_cfg_io(void)
{
	printf("fpga rsb cfg init");
	writel(0x77777733, 0x0300b024);
	writel(0x77772277, 0x07022000);
}

#else
static void rsb_cfg_io(void)
{
	uint reg, val;

	/*PL0,PL1 cfg 2*/
	reg = SUNXI_RPIO_BASE+0x0;
	val = readl(reg)  & (~0xff);
	val |= 0x22;
	writel(val, reg);

	/*PL0,PL1 drv 2*/
	reg = SUNXI_RPIO_BASE+0x14;
	val = readl(reg)  & (~0xf);
	val |= 0xa;
	writel(val, reg);

	/*PL0,PL1 pull up 1*/
	reg = SUNXI_RPIO_BASE+0x1c;
	val = readl(reg)  & (~0xf);
	val |= 0x5;
	writel(val, reg);

}
#endif



static void rsb_bus_gating_reset(void)
{
	u32 reg_value;
	u32 reg_addr;

	reg_addr = SUNXI_RRSB_BRG_REG;

	/*rsb reset*/
	reg_value = readl(reg_addr);
	reg_value |=  (1 << SUNXI_RRSB_RST_BIT);
	writel(reg_value, reg_addr);

	/*rsb gating*/
	reg_value |= (1 << SUNXI_RRSB_GATING_BIT);
	writel(reg_value, reg_addr);

}

static void rsb_set_clk(u32 sck)
{
	u32 src_clk = 0;
	u32 div = 0;
	u32 cd_odly = 0;
	u32 rval = 0;

	src_clk = 24000000;

	div = src_clk/sck/2;
	if (0 == div) {
		div = 1;
		rsb_printk("Source clock is too low\n");
	} else if (div > 256) {
		div = 256;
		rsb_printk("Source clock is too high\n");
	}
	div--;
	cd_odly = div >> 1;
	/* cd_odly = 1; */
	if (!cd_odly)
		cd_odly = 1;
	rval = div | (cd_odly << 8);
	writel(rval, RSB_REG_CCR);
}


static s32 rsb_send_initseq(u32 slave_addr, u32 reg, u32 data)
{

	while (readl(RSB_REG_STAT) &
	       (RSB_LBSY_INT | RSB_TERR_INT | RSB_TOVER_INT)) {
		rsb_printk("status err\n");
	}

	rsbc.rsb_busy = 1;
	rsbc.rsb_flag = 0;
	rsbc.rsb_load_busy = 0;
	writel(RSB_PMU_INIT|(slave_addr << 1)				\
					|(reg << PMU_MOD_REG_ADDR_SHIFT)			\
					|(data << PMU_INIT_DAT_SHIFT), 				\
					RSB_REG_PMCR);
	while (readl(RSB_REG_PMCR) & RSB_PMU_INIT) {
	}

	while (rsbc.rsb_busy) {

		/* istat will be optimize? */
		u32 istat = readl(RSB_REG_STAT);

		if (istat & RSB_LBSY_INT) {
			rsbc.rsb_load_busy = 1;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TERR_INT) {
			rsbc.rsb_flag = (istat >> 8) & 0xffff;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TOVER_INT) {
			rsbc.rsb_busy = 0;
			writel(istat, RSB_REG_STAT);
		}
	}

	if (rsbc.rsb_load_busy) {
		rsb_printk("Load busy\n");
		return RET_FAIL;
	}

	if (rsbc.rsb_flag) {
		rsb_printk("rsb write failed, flag 0x%x:%s%s%s%s%s !!\n",
			   rsbc.rsb_flag,
			   rsbc.rsb_flag & ERR_TRANS_1ST_BYTE ? " 1STE " : "",
			   rsbc.rsb_flag & ERR_TRANS_2ND_BYTE ? " 2NDE " : "",
			   rsbc.rsb_flag & ERR_TRANS_3RD_BYTE ? " 3RDE " : "",
			   rsbc.rsb_flag & ERR_TRANS_4TH_BYTE ? " 4THE " : "",
			   rsbc.rsb_flag & ERR_TRANS_RT_NO_ACK ? " NOACK "
							       : "");
		return -rsbc.rsb_flag;
	}

	return 0;
}


static s32 set_run_time_addr(u32 saddr, u32 rtsaddr)
{

	while (readl(RSB_REG_STAT) &
	       (RSB_LBSY_INT | RSB_TERR_INT | RSB_TOVER_INT)) {
		rsb_printk("status err\n");
	}

	rsbc.rsb_busy = 1;
	rsbc.rsb_flag = 0;
	rsbc.rsb_load_busy = 0;
	writel((saddr<<RSB_SADDR_SHIFT)						\
					|(rtsaddr<<RSB_RTSADDR_SHIFT),				\
					RSB_REG_SADDR);
	writel(RSB_CMD_SET_RTSADDR, RSB_REG_CMD);
	writel(readl(RSB_REG_CTRL)|RSB_START_TRANS, RSB_REG_CTRL);

	while (rsbc.rsb_busy) {

		/* istat will be optimize? */
		u32 istat = readl(RSB_REG_STAT);

		if (istat & RSB_LBSY_INT) {
			rsbc.rsb_load_busy = 1;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TERR_INT) {
			rsbc.rsb_flag = (istat >> 8) & 0xffff;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TOVER_INT) {
			rsbc.rsb_busy = 0;
			writel(istat, RSB_REG_STAT);
		}
	}

	if (rsbc.rsb_load_busy) {
		rsb_printk("Load busy\n");
		return RET_FAIL;
	}

	if (rsbc.rsb_flag) {
		rsb_printk("rsb set run time address failed, flag "
			   "0x%x:%s%s%s%s%s !!\n",
			   rsbc.rsb_flag,
			   rsbc.rsb_flag & ERR_TRANS_1ST_BYTE ? " 1STE " : "",
			   rsbc.rsb_flag & ERR_TRANS_2ND_BYTE ? " 2NDE " : "",
			   rsbc.rsb_flag & ERR_TRANS_3RD_BYTE ? " 3RDE " : "",
			   rsbc.rsb_flag & ERR_TRANS_4TH_BYTE ? " 4THE " : "",
			   rsbc.rsb_flag & ERR_TRANS_RT_NO_ACK ? " NOACK "
							       : "");
		return -rsbc.rsb_flag;
	}
	return 0;
}


s32 rsb_write(u32 rtsaddr, u32 daddr, u8 *data, u32 len)
{
	u32 cmd = 0;
	u32 dat = 0;
	s32 i	= 0;
	if (len > 4 || len == 0 || len == 3) {
		rsb_printk("error length %d\n", len);
		return -1;
	}
	if (NULL == data) {
		rsb_printk("data should not be NULL\n");
		return -1;
	}

	while (readl(RSB_REG_STAT) &
	       (RSB_LBSY_INT | RSB_TERR_INT | RSB_TOVER_INT)) {
		rsb_printk("status err\n");
	}

	rsbc.rsb_flag = 0;
	rsbc.rsb_busy = 1;
	rsbc.rsb_load_busy = 0;

	writel(rtsaddr << RSB_RTSADDR_SHIFT, RSB_REG_SADDR);
	writel(daddr, RSB_REG_DADDR0);

	for (i = 0; i < len; i++) {
		dat |= data[i] << (i * 8);
	}

	writel(dat, RSB_REG_DATA0);

	switch (len) {
	case 1:
		cmd = RSB_CMD_BYTE_WRITE;
		break;
	case 2:
		cmd = RSB_CMD_HWORD_WRITE;
		break;
	case 4:
		cmd = RSB_CMD_WORD_WRITE;
		break;
	default:
		break;
	}
	writel(cmd, RSB_REG_CMD);

	writel(readl(RSB_REG_CTRL) | RSB_START_TRANS, RSB_REG_CTRL);
	while (rsbc.rsb_busy) {

		/* istat will be optimize? */
		u32 istat = readl(RSB_REG_STAT);

		if (istat & RSB_LBSY_INT) {
			rsbc.rsb_load_busy = 1;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TERR_INT) {
			rsbc.rsb_flag = (istat >> 8) & 0xffff;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TOVER_INT) {
			rsbc.rsb_busy = 0;
			writel(istat, RSB_REG_STAT);
		}
	}

	if (rsbc.rsb_load_busy) {
		rsb_printk("Load busy\n");
		return RET_FAIL;
	}

	if (rsbc.rsb_flag) {
		rsb_printk("rsb write failed, flag 0x%x:%s%s%s%s%s !!\n",
			   rsbc.rsb_flag,
			   rsbc.rsb_flag & ERR_TRANS_1ST_BYTE ? " 1STE " : "",
			   rsbc.rsb_flag & ERR_TRANS_2ND_BYTE ? " 2NDE " : "",
			   rsbc.rsb_flag & ERR_TRANS_3RD_BYTE ? " 3RDE " : "",
			   rsbc.rsb_flag & ERR_TRANS_4TH_BYTE ? " 4THE " : "",
			   rsbc.rsb_flag & ERR_TRANS_RT_NO_ACK ? " NOACK "
							       : "");
		return -rsbc.rsb_flag;
	}

	return 0;
}


s32 rsb_read(u32 rtsaddr, u32 daddr, u8 *data, u32 len)
{
	u32 cmd = 0;
	u32 dat = 0;
	s32 i	= 0;
	if (len > 4 || len == 0 || len == 3) {
		rsb_printk("error length %d\n", len);
		return -1;
	}
	if (NULL == data) {
		rsb_printk("data should not be NULL\n");
		return -1;
	}

	while (readl(RSB_REG_STAT) &
	       (RSB_LBSY_INT | RSB_TERR_INT | RSB_TOVER_INT)) {
		rsb_printk("status err\n");
	}

	rsbc.rsb_flag = 0;
	rsbc.rsb_busy = 1;
	rsbc.rsb_load_busy = 0;

	writel(rtsaddr << RSB_RTSADDR_SHIFT, RSB_REG_SADDR);
	writel(daddr, RSB_REG_DADDR0);

	switch (len) {
	case 1:
		cmd = RSB_CMD_BYTE_READ;
		break;
	case 2:
		cmd = RSB_CMD_HWORD_READ;
		break;
	case 4:
		cmd = RSB_CMD_WORD_READ;
		break;
	default:
		break;
	}
	writel(cmd, RSB_REG_CMD);

	writel(readl(RSB_REG_CTRL) | RSB_START_TRANS, RSB_REG_CTRL);
	while (rsbc.rsb_busy) {

		u32 istat = readl(RSB_REG_STAT);

		if (istat & RSB_LBSY_INT) {
			rsbc.rsb_load_busy = 1;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TERR_INT) {
			rsbc.rsb_flag = (istat >> 8) & 0xffff;
			writel(istat, RSB_REG_STAT);
			break;
		}

		if (istat & RSB_TOVER_INT) {
			rsbc.rsb_busy = 0;
			writel(istat, RSB_REG_STAT);
		}
	}

	if (rsbc.rsb_load_busy) {
		rsb_printk("Load busy\n");
		return RET_FAIL;
	}

	if (rsbc.rsb_flag) {
		rsb_printk(
		    "rsb read failed, flag 0x%x:%s%s%s%s%s !!\n", rsbc.rsb_flag,
		    rsbc.rsb_flag & ERR_TRANS_1ST_BYTE ? " 1STE " : "",
		    rsbc.rsb_flag & ERR_TRANS_2ND_BYTE ? " 2NDE " : "",
		    rsbc.rsb_flag & ERR_TRANS_3RD_BYTE ? " 3RDE " : "",
		    rsbc.rsb_flag & ERR_TRANS_4TH_BYTE ? " 4THE " : "",
		    rsbc.rsb_flag & ERR_TRANS_RT_NO_ACK ? " NOACK " : "");
		return -rsbc.rsb_flag;
	}

	dat = readl(RSB_REG_DATA0);
	for (i = 0; i < len; i++) {
		data[i] = (dat >> (i * 8)) & 0xff;
	}

	return 0;
}

s32 rsb_init(u32 saddr, u32 rtaddr)
{
	int ret;

	rsbc.rsb_flag = 0;
	rsbc.rsb_busy = 0;
	rsbc.rsb_load_busy	= 0;

	rsb_cfg_io();
	rsb_bus_gating_reset();

	writel(RSB_SOFT_RST, RSB_REG_CTRL);

	/*rsb clk = 400Khz*/
	rsb_set_clk(400000);
	ret = rsb_send_initseq(0x00, 0x3e, 0x7c);
	if (ret) {
		return ret;
	}
	/*rsb clk = 3Mhz*/
	rsb_set_clk(RSB_SCK);
	printf("rsb_send_initseq: rsb clk 400Khz -> 3Mhz\n");
	/* rsb runtime addr*/
	ret = set_run_time_addr(saddr, rtaddr);
	return ret;
}

s32 rsb_exit(void)
{
	return 0;
}
